package api;

import api.utils.*;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.github.crab2died.ExcelUtils;
import com.github.crab2died.exceptions.Excel4JException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
public class ApiTest2 {
    public static final String dir =System.getProperty("user.dir")+File.separator+"data"+File.separator;
    public static final String  filePath =dir+"apitest3.xlsx";
    public final static ExecutorService executorService = Executors.newFixedThreadPool(50); //线程池对象
    @SneakyThrows
    public static void main(String[] args) {
        HttpClientUtils.openProxy=false;

        log.info("系统启动");

        List<String> allBuniess = ExcelToMapUtils.getAllBuniess(filePath);

        SimpleDateFormat format0 = new SimpleDateFormat("yyyy-MM-dd(HH_mm)");
        String time = format0.format(System.currentTimeMillis());
        System.out.println(time);
        String resultPath=dir+"result_"+time+".xlsx";

        ExcelWriter excelWriter = EasyExcel.write(resultPath).build();
        //三层循环 思路
        List<String> pathList =new ArrayList<>();
        pathList.add(resultPath);

        ArrayList<TestStatistics> statisticsList = new ArrayList<>();
        for (int i = 0; i < allBuniess.size(); i++) { //业务
            //登录业务参数
            //参数读取问题
            String buniess = allBuniess.get(i);
            TestStatistics testStatistics=new TestStatistics();
            testStatistics.setBuniess(buniess);
            List<Map<String, Object>> listMap = ExcelToMapUtils.importExcel(filePath, i+1); //参数覆盖
            List<TestResult> bus_results=new ArrayList<>();
            CountDownLatch latch = new CountDownLatch(listMap.size());
            for (Map<String, Object> map : listMap) { //独立的map 100
//                new Thread(()->{
//                    List<TestResult> results = testCase(buniess, map, testStatistics);  //一组参数
//                    bus_results.addAll(results);
//                    latch.countDown(); //--
//                }).start();
                //线程池优化
                executorService.submit(()->{
                    List<TestResult> results = testCase(buniess, map, testStatistics);  //一组参数
                    bus_results.addAll(results);
                    map.clear();
                    latch.countDown();
                });

//                List<TestResult> results = testCase(buniess, map, testStatistics);  //一组参数
//                bus_results.addAll(results);
            }

            // count =0
            latch.await(3,TimeUnit.MINUTES); //最大等待时间
            //线程等待
            if(!bus_results.isEmpty()){
                WriteSheet writeSheet = EasyExcel.writerSheet(i+1, buniess).head(TestResultEasyExcel.class).build();
                excelWriter.write(bus_results,writeSheet);
                testStatistics.setStatistics(getStatisticsFormat(testStatistics));
                if(testStatistics.getError()>0){
                    List<TestCase> testCases = testStatistics.getRetry();
                    List<String> urlList = testCases.stream().map(TestCase::getUrl).
                            distinct().collect(Collectors.toList());
                    testStatistics.setErrorApis(urlList.toString());
                }
                statisticsList.add(testStatistics);
            }
        }
        WriteSheet writeSheet = EasyExcel.writerSheet("统计").head(TestStatistics.class).build();
        excelWriter.write(statisticsList,writeSheet);

        retry(statisticsList,excelWriter);

        excelWriter.finish();  //close


        //统一发邮件
        EmailUtils.sendEmailsWithAttachments("测试结果","请查收",pathList);

        executorService.shutdownNow(); //kill -9
    }

    private static int retryCount=0; //已经重试次数

    private static int retryMax=PropertiesUtils.getInt("retry.max");

    private static boolean retrySleep=PropertiesUtils.getBoolean("retry.sleep");

    private static int sleepTime=PropertiesUtils.getInt("retry.sleepTime");

    public static void retry(List<TestStatistics> statisticsList,ExcelWriter excelWriter){
        retry(statisticsList,excelWriter,TestStatistics.class);
    }

    public static void retry(List<TestStatistics> statisticsList,ExcelWriter excelWriter,Class clz) {
        System.out.println("调用次数------"+retryCount);
        log.info("调用次数------{}",retryCount);
       if(retryCount++>=retryMax){
           return;
       }
       //控制重试频率
       if(retrySleep){
           try {
               TimeUnit.SECONDS.sleep(sleepTime*retryCount);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
        List<TestStatistics> retryList =new ArrayList<>();
        for (int i = 0; i < statisticsList.size(); i++) {
            TestStatistics testStatistics = statisticsList.get(i);
            if(testStatistics.getError()>0){
                TestStatisticsDetail statisticsReTry = new TestStatisticsDetail();
                statisticsReTry.setBuniess(testStatistics.getBuniess());
                if(testStatistics instanceof TestStatisticsDetail){
                    TestStatisticsDetail detail =(TestStatisticsDetail)testStatistics;
                    statisticsReTry.setScene(detail.getScene());
                }
                List<TestCase> testCaseList = testStatistics.getRetry();
                CountDownLatch latch = new CountDownLatch(testCaseList.size());
                for (int j = 0; j < testCaseList.size(); j++) {
                    int finalJ = j;
                    executorService.submit(()->{
                        HttpResult httpResult = null;
                        TestCase testCase = testCaseList.get(finalJ);
                        TestResult result=new TestResult();
                        if("get".equals(testCase.getType())){
                            httpResult = HttpClientUtils.doGet(testCase.getUrl(), testCase.getHeaders());
                        }else if("post".equals(testCase.getType())){
                            httpResult=HttpClientUtils.doPost(testCase.getUrl(),testCase.getParams(),testCase.getHeaders());
                        }else if("postjson".equals(testCase.getType())){
                            httpResult=HttpClientUtils.doPostJson(testCase.getUrl(),testCase.getParams(),testCase.getHeaders());
                        }
                        if(httpResult.getCode()==200){
                            statisticsReTry.setSuccess(statisticsReTry.getSuccess()+1);
                        }else{
                            statisticsReTry.setError(statisticsReTry.getError()+1);
                            statisticsReTry.addRetry(testCase);
                        }
                        latch.countDown();
                    });
                }
                try {
                    latch.await(3,TimeUnit.MINUTES);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                statisticsReTry.setStatistics(getStatisticsFormat(statisticsReTry));
                if(statisticsReTry.getError()>0){
                    List<TestCase> testCases = statisticsReTry.getRetry();
                    List<String> urlList = testCases.stream().map(TestCase::getUrl).
                            distinct().collect(Collectors.toList());
                    statisticsReTry.setErrorApis(urlList.toString());
                }
                retryList.add(statisticsReTry);
            }
        }
        if(!retryList.isEmpty()){
            retryList.sort(Comparator.comparing(TestStatistics::getBuniess));
            WriteSheet writeSheet = EasyExcel.writerSheet("重试_"+retryCount).head(clz).build();
            excelWriter.write(retryList,writeSheet);
            //递归
            retry(retryList,excelWriter,clz);
        }

    }

    public static String getStatisticsFormat(TestStatistics testStatistics){
        //DecimalFormat df = new DecimalFormat("0.00");
        DecimalFormat df = new DecimalFormat("0.0");
        int success=testStatistics.getSuccess();
        int all =testStatistics.getSuccess()+testStatistics.getError();
        String format = df.format(((double)success/all)*100);
        return format+"%";
    }

//    private static String saveResultExcel(String buniess, List<TestResult> results) throws Excel4JException, IOException {
//        if(results.isEmpty()){
//            return "";
//        }
//        SimpleDateFormat format0 = new SimpleDateFormat("yyyy-MM-dd(HH_mm)");
//        String time = format0.format(System.currentTimeMillis());
//        System.out.println(time);
//        String path=dir+buniess+"_result_"+time+".xlsx";
//       // ExcelUtils.getInstance().exportObjects2Excel(results, TestResult.class, path);
//
//        EasyExcel.write(path, TestResultEasyExcel.class).sheet("测试结果").doWrite(results);
//        return path;
//    }

    /**
     * @param buniess 业务
     * @param map 参数
     */
    public static List<TestResult> testCase(String buniess, Map<String,Object> map,TestStatistics testStatistics){

        List<TestCase> testCases  =getTestCase(buniess); //全部 -》业务关联 参数 过滤

        List<TestResult> results=new ArrayList<>();
        int order=1; //相对编号
        //testcase参数替换问题
        for (TestCase testCase : testCases) { //串行
            HttpResult httpResult = null;
            TestResult result=new TestResult();
            replace(testCase,map);
            if("get".equals(testCase.getType())){
                httpResult = HttpClientUtils.doGet(testCase.getUrl(), testCase.getHeaders());
            }else if("post".equals(testCase.getType())){
                httpResult=HttpClientUtils.doPost(testCase.getUrl(),testCase.getParams(),testCase.getHeaders());
            }else if("postjson".equals(testCase.getType())){
                httpResult=HttpClientUtils.doPostJson(testCase.getUrl(),testCase.getParams(),testCase.getHeaders());
            }
            System.out.println(testCase+" "+httpResult);


            //检查点
            if(httpResult.getCode()==200){
               addCheckPonit(order,httpResult.getMsg(),map,result);
            }

            if(httpResult.getCode()==200){
                testStatistics.setSuccess(testStatistics.getSuccess()+1);
            }else{
                testStatistics.setError(testStatistics.getError()+1);
                testStatistics.addRetry(testCase);
            }
            order++;
            //提取json结果
            if(httpResult.getCode()==200&&StringUtils.isNotBlank(testCase.getJsonCollect())){
                ParamsUtils.addJsonCollectToMap(testCase.getJsonCollect(),httpResult.getMsg(),map);
            }
            if(httpResult.getCode()==200&&StringUtils.isNotBlank(testCase.getRegxCollect())){
                ParamsUtils.addRegxCollectToMap(testCase.getRegxCollect(),httpResult.getMsg(),map);
            }
            result.setCode(httpResult.getCode());
            result.setHttpResult(httpResult.getMsg());
            result.setBuniess(buniess);
            try {
                BeanUtils.copyProperties(result,testCase);
            } catch (Exception e) {
                e.printStackTrace();
            }

            results.add(result);
        }
        log.info("业务{},参数{}执行完毕",buniess, map);
       // map.clear();
        return results;
    }

    //1_equal={"msg":"登录成功","uid":"9CC972DFA2D4481F89841A46FD1B3E7B","code":"1"};2_contains="code":"1";
    public static void addCheckPonit(int order, String msg, Map<String, Object> map,TestResult result) {
        String checkpoint = MapUtils.getString(map, "checkpoint", "");
        result.setScene(MapUtils.getString(map, "scene", ""));
        if (StringUtils.isNotBlank(checkpoint)) {
            Map<String, Object> stringObjectMap = StringToMapUtils.convert2(checkpoint);
            System.out.println(stringObjectMap);
            stringObjectMap.forEach((k,v)->{
                if(k.startsWith(""+order)){
                    if(k.contains("equal")){
                        result.setCheckpoint(k+"="+v);
                        //三元表达式
                        result.setCheckpointResult(msg.equals(v)==true?"检查成功":"检查失败");
                    }else if(k.contains("contains")){
                        result.setCheckpoint(k+"="+v);
                        result.setCheckpointResult(msg.contains(""+v)==true?"检查成功":"检查失败");
                    }
                }
            });
        }
    }


    @SneakyThrows
    private static List<TestCase> getTestCase(String buniess) {
        //反射技术 你把对象的定义给Excel4J框架，框架帮你new
        List<TestCase> list = ExcelUtils.getInstance().readExcel2Objects(filePath, TestCase.class);
        //  List<TestCase> list = ExcelUtils.getInstance().readExcel2Objects(filePath, TestCase.class,1);
        //所有sheet
        // list.forEach(d-> System.out.println(d));
        //lombok 工具代码提示 //jdk8  内存排序
        List<TestCase> testCases = list.stream().filter(d -> d.getRun().equals("是")&&buniess.equals(d.getBusiness()))
                .sorted(Comparator.comparing(TestCase::getOrder)).
                        collect(Collectors.toList());
//        testCases.forEach(d-> System.out.println(d));
        return testCases;
    }



    public static void replace(TestCase testCase, Map<String, Object> map) {
        testCase.setParams(ParamsUtils.replace(testCase.getParams(),map));
        testCase.setHeaders(ParamsUtils.replace(testCase.getHeaders(),map));
        testCase.setUrl(ParamsUtils.replace(testCase.getUrl(),map));
    }
}
